本篇同步發文在個人Blog: 一袋.NET要扛幾樓?打造容器化的ASP.NET Core網站!系列文章 - (26) 建立購物車系統 - 9
在WebMvc專案的Services新增CartService,並實作ICartService,主要是實作與購物車服務Api的串接:
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using WebMvc.Infrastructure;
using WebMvc.Models;
using WebMvc.Models.CartModels;
namespace WebMvc.Services
{
public class CartService : ICartService
{
private readonly IOptionsSnapshot<AppSettings> _settings;
private readonly IHttpClient _apiClient;
private readonly string _remoteServiceBaseUrl;
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly ILogger<CartService> _logger;
public CartService(
IOptionsSnapshot<AppSettings> settings,
IHttpClient httpClient,
ILogger<CartService> logger,
IHttpContextAccessor httpContextAccessor)
{
_settings = settings;
_remoteServiceBaseUrl = $"{_settings.Value.CartUrl}/api/v1/cart";
_httpContextAccessor = httpContextAccessor;
_apiClient = httpClient;
_logger = logger;
}
public async Task AddItemToCartAsync(ApplicationUser user, CartItem product)
{
var cart = await GetCartAsync(user);
_logger.LogDebug("user name" + user.Id);
if (cart == null)
{
cart = new Cart()
{
BuyerId = user.Id,
Items = new List<CartItem>()
};
}
var cartItem = cart.Items
.Where(p => p.ProductId == product.ProductId)
.FirstOrDefault();
if (cartItem == null)
{
cart.Items.Add(product);
}
else
{
cartItem.Quantity += 1;
}
await UpdateCartAsync(cart);
}
public async Task ClearCartAsync(ApplicationUser user)
{
var token = await GetUserTokenAsync();
var cleanCartUri = ApiPaths.Cart.CleanCart(_remoteServiceBaseUrl, user.Id);
_logger.LogDebug("Clean cart uri : " + cleanCartUri);
var response = await _apiClient.DeleteAsync(cleanCartUri);
_logger.LogDebug("cart cleaned");
}
public async Task<Cart> GetCartAsync(ApplicationUser user)
{
var token = await GetUserTokenAsync();
_logger.LogInformation("we are in get cart and user id " + user.Id);
_logger.LogInformation(_remoteServiceBaseUrl);
var getCartUri = ApiPaths.Cart.GetCart(_remoteServiceBaseUrl, user.Id);
_logger.LogInformation(getCartUri);
var dataString = await _apiClient.GetStringAsync(getCartUri, token);
_logger.LogInformation(dataString);
var response = JsonConvert.DeserializeObject<Cart>(dataString.ToString()) ??
new Cart()
{
BuyerId = user.Id
};
return response;
}
public async Task<Cart> SetQuantitiesAsync(ApplicationUser user, Dictionary<string, int> quantities)
{
var cart = await GetCartAsync(user);
cart.Items.ForEach(x =>
{
if (quantities.TryGetValue(x.Id, out var quantity))
{
x.Quantity = quantity;
}
});
return cart;
}
public async Task<Cart> UpdateCartAsync(Cart cart)
{
var token = await GetUserTokenAsync();
_logger.LogDebug("Service url: " + _remoteServiceBaseUrl);
var updateCartUri = ApiPaths.Cart.UpdateCart(_remoteServiceBaseUrl);
_logger.LogDebug("Update cart url: " + updateCartUri);
var response = await _apiClient.PostAsync(updateCartUri, cart, token);
response.EnsureSuccessStatusCode();
return cart;
}
private async Task<string> GetUserTokenAsync()
{
var context = _httpContextAccessor.HttpContext;
return await context.GetTokenAsync("access_token");
}
}
}
在WebMvc專案的Infrastructure修改ApiPaths.cs,增加購物車Api的Pattern:
namespace WebMvc.Infrastructure
{
public static class ApiPaths
{
public static class Cart
{
public static string GetCart(string baseUri, string cartId)
{
return $"{baseUri}/{cartId}";
}
public static string UpdateCart(string baseUri)
{
return baseUri;
}
public static string CleanCart(string baseUri, string cartId)
{
return $"{baseUri}/{cartId}";
}
}
public static class Catalog
{
public static string GetAllCatalogItems(string baseUri, int pageIndex, int pageSize, int? type)
{
string typeQueryString = "";
if (type.HasValue)
{
typeQueryString = type.Value.ToString();
}
return $"{baseUri}items?catalogTypeId={typeQueryString}&pageIndex={pageIndex}&pageSize={pageSize}";
}
public static string GetAllTypes(string baseUri)
{
return $"{baseUri}catalogTypes";
}
}
}
}
在WebMvc專案的Infrastructure修改IHttpClient介面和CustomHttpClient類別,增加了JWT的token參數:
using System.Net.Http;
using System.Threading.Tasks;
namespace WebMvc.Infrastructure
{
public interface IHttpClient
{
Task<string> GetStringAsync(string uri, string authorizationToken = null, string authorizationMethod = "Bearer");
Task<HttpResponseMessage> PostAsync<T>(string uri, T item, string authorizationToken = null, string authorizationMethod = "Bearer");
Task<HttpResponseMessage> DeleteAsync(string uri, string authorizationToken = null, string authorizationMethod = "Bearer");
Task<HttpResponseMessage> PutAsync<T>(string uri, T item, string authorizationToken = null, string authorizationMethod = "Bearer");
}
}
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
namespace WebMvc.Infrastructure
{
public class CustomHttpClient : IHttpClient
{
private HttpClient _client;
private ILogger<CustomHttpClient> _logger;
public CustomHttpClient( ILogger<CustomHttpClient> logger)
{
_client = new HttpClient();
_logger = logger;
}
public async Task<HttpResponseMessage> DeleteAsync(string uri, string authorizationToken = null, string authorizationMethod = "Bearer")
{
var requestMessage = new HttpRequestMessage(HttpMethod.Delete, uri);
if (authorizationToken != null)
{
requestMessage.Headers.Authorization = new AuthenticationHeaderValue(authorizationMethod, authorizationToken);
}
return await _client.SendAsync(requestMessage);
}
public async Task<string> GetStringAsync(string uri, string authorizationToken = null, string authorizationMethod = "Bearer")
{
var requestMessage = new HttpRequestMessage(HttpMethod.Get, uri);
if (authorizationToken != null)
{
requestMessage.Headers.Authorization = new AuthenticationHeaderValue(authorizationMethod, authorizationToken);
}
var response = await _client.SendAsync(requestMessage);
return await response.Content.ReadAsStringAsync();
}
public async Task<HttpResponseMessage> PostAsync<T>(string uri, T item, string authorizationToken = null, string authorizationMethod = "Bearer")
{
return await DoPostPutAsync(HttpMethod.Post, uri, item, authorizationToken, authorizationMethod);
}
public async Task<HttpResponseMessage> PutAsync<T>(string uri, T item, string authorizationToken = null, string authorizationMethod = "Bearer")
{
return await DoPostPutAsync(HttpMethod.Put, uri, item, authorizationToken, authorizationMethod);
}
private async Task<HttpResponseMessage> DoPostPutAsync<T>(HttpMethod method, string uri, T item, string authorizationToken = null, string authorizationMethod = "Bearer")
{
if (method != HttpMethod.Post && method != HttpMethod.Put)
{
throw new ArgumentException("Value must be either post or put.", nameof(method));
}
var requestMessage = new HttpRequestMessage(method, uri)
{
Content = new StringContent(JsonConvert.SerializeObject(item), System.Text.Encoding.UTF8, "application/json")
};
if (authorizationToken != null)
{
requestMessage.Headers.Authorization = new AuthenticationHeaderValue(authorizationMethod, authorizationToken);
}
var response = await _client.SendAsync(requestMessage);
if (response.StatusCode == System.Net.HttpStatusCode.InternalServerError)
{
throw new HttpRequestException();
}
return response;
}
}
}